package com.xwintop.xTransfer.filter.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.xwintop.xJavaFxTool.utils.SpringUtil;
import com.xwintop.xTransfer.common.MsgLogger;
import com.xwintop.xTransfer.common.model.LOGKEYS;
import com.xwintop.xTransfer.common.model.LOGVALUES;
import com.xwintop.xTransfer.common.model.Msg;
import com.xwintop.xTransfer.filter.bean.FilterConfig;
import com.xwintop.xTransfer.filter.bean.FilterConfigSpelExpressionParser;
import com.xwintop.xTransfer.filter.service.Filter;
import com.xwintop.xTransfer.filter.util.FilterServiceUtil;
import com.xwintop.xTransfer.messaging.DefaultMessage;
import com.xwintop.xTransfer.messaging.IContext;
import com.xwintop.xTransfer.messaging.IMessage;
import com.xwintop.xcore.commons.CacheUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Scope;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * @ClassName: FilterSpelExpressionParserImpl
 * @Description: Spel表达式引擎实现类
 * @author: xufeng
 * @date: 2021/9/17 10:53
 */

@Service("filterSpelExpressionParser")
@Scope("prototype")
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class FilterSpelExpressionParserImpl implements Filter {
    private FilterConfigSpelExpressionParser filterConfigSpelExpressionParser;

    @Resource(name = "cacheUtil")
    private CacheUtil cacheUtil;

    private long groovyScriptFileLastModified = Long.MIN_VALUE;

    private Expression expression;

    @Override
    public void doFilter(IContext ctx, Map params) throws Exception {
        for (IMessage iMessage : ctx.getMessages()) {
            if (!FilterServiceUtil.checkFilterMessageIsResume(filterConfigSpelExpressionParser, iMessage, params)) {
                continue;
            }
            doFilter(iMessage, params);
        }
    }

    public void doFilter(IMessage msg, Map params) throws Exception {
        EvaluationContext context = new StandardEvaluationContext();
        context.setVariable("message", msg);
        context.setVariable("params", params);
        context.setVariable("args", filterConfigSpelExpressionParser.getArgs());
        context.setVariable("applicationContext", SpringUtil.getApplicationContext());
        context.setVariable(DefaultMessage.RDBMS_DATA_LIST, msg.getProperty(DefaultMessage.RDBMS_DATA_LIST));
        if (StringUtils.isNotEmpty(filterConfigSpelExpressionParser.getScriptString())) {
            //解析表达式，如果表达式是一个模板表达式，需要为解析传入模板解析器上下文。
            if (expression == null) {
                ExpressionParser expressionParser = new SpelExpressionParser();
                expression = expressionParser.parseExpression(filterConfigSpelExpressionParser.getScriptString());
            }
            Object value = expression.getValue(context);
            setValueToMsg(msg, value);
        } else if (StringUtils.isNotEmpty(filterConfigSpelExpressionParser.getScriptFilePath())) {
            if (filterConfigSpelExpressionParser.getScriptFileCacheTime() < 0) {
                initExpressionParser();
            } else {
//                if (cacheUtil.get(CacheUtil.gateWayName, "ScriptFileCache_" + filterConfigSpelExpressionParser.getId()) == null) {
                    initExpressionParser();
//                    cacheUtil.set("ScriptFileCache_" + filterConfigSpelExpressionParser.getId(), 1, filterConfigSpelExpressionParser.getScriptFileCacheTime(), filterConfigSpelExpressionParser.getScriptFileCacheTime());
//                }
            }
            Object value = expression.getValue(context);
            setValueToMsg(msg, value);
        } else {
            log.warn("{} 未填写模板内容或模板文件路径", filterConfigSpelExpressionParser.getId());
        }
        Msg msgLogInfo = new Msg(LOGVALUES.EVENT_MSG_FILTER, LOGVALUES.CHANNEL_TYPE_SPEL_EXPRESSION_PARSER, filterConfigSpelExpressionParser.getId(), params.get(LOGKEYS.JOB_ID), params.get(LOGKEYS.JOB_SEQ), msg.getId());
        msgLogInfo.put(LOGKEYS.MSG_TAG, msg.getFileName());
        msgLogInfo.put(LOGKEYS.MSG_LENGTH, ArrayUtils.getLength(msg.getMessage()));
        msgLogInfo.put(LOGKEYS.RECEIVER_TYPE, msg.getProperty(LOGKEYS.RECEIVER_TYPE));
        msgLogInfo.put(LOGKEYS.RECEIVER_ID, msg.getProperty(LOGKEYS.RECEIVER_ID));
        MsgLogger.info(msgLogInfo.toMap(), filterConfigSpelExpressionParser.isLogUpload());
    }

    //设置表达式值至消息中
    private void setValueToMsg(IMessage msg, Object value) {
        if (value == null) {
            msg.setMessage("".getBytes());
        } else if (value instanceof String) {
            msg.setMessage(((String) value).getBytes());
        } else {
            msg.setMessage(JSON.toJSONString(value, SerializerFeature.WriteMapNullValue).getBytes());
        }
    }

    private void initExpressionParser() throws Exception {
        File scriptFile = new File(filterConfigSpelExpressionParser.getScriptFilePath());
        long lastModified = scriptFile.lastModified();
        if (groovyScriptFileLastModified != lastModified) {
            String scriptText = FileUtils.readFileToString(scriptFile, StandardCharsets.UTF_8);
            ExpressionParser expressionParser = new SpelExpressionParser();
            expression = expressionParser.parseExpression(scriptText);
            groovyScriptFileLastModified = lastModified;
        }
    }

    @Override
    public void setFilterConfig(FilterConfig filterConfig) throws Exception {
        this.filterConfigSpelExpressionParser = (FilterConfigSpelExpressionParser) filterConfig;
    }

    @Override
    public void destroy() {
        if (filterConfigSpelExpressionParser.getScriptFileCacheTime() >= 0) {
            cacheUtil.remove("ScriptFileCache_" + filterConfigSpelExpressionParser.getId());
        }
        expression = null;
        groovyScriptFileLastModified = Long.MIN_VALUE;
    }
}
